package com.openswimsoftware.orm.model.query;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

import com.openswimsoftware.orm.ORM;
import com.openswimsoftware.orm.model.Model;
import com.openswimsoftware.orm.model.exception.InvalidModelField;
import com.openswimsoftware.orm.model.exception.InvalidModelRelationship;
import com.openswimsoftware.orm.model.query.order.OrderDirection;
import com.openswimsoftware.orm.model.query.order.QueryOrderBy;
import com.openswimsoftware.orm.model.query.where.QueryWhere;
import com.openswimsoftware.orm.model.query.where.WhereCombo;
import com.openswimsoftware.orm.model.query.where.WhereOperator;

import android.content.Context;
import android.database.Cursor;
import android.util.Log;

public class ModelFind<E extends  Model<E>> extends ModelQuery<E, ModelFind<E>> {
	
	private QueryOrderBy orderBy;
	
	public ModelFind(Context context, Class<E> modelType, E model)
	{
		super(context, modelType, model);
	}
	
	public ModelFind<E> where(String field, WhereOperator operator, String value) throws InvalidModelField
	{
		this.where(WhereCombo.AND, field, operator, value);
		
		return this;
	}
	
	public ModelFind<E> where(WhereCombo combo, String field, WhereOperator operator, String value) throws InvalidModelField
	{
		this.whereList.add(new QueryWhere(combo, ORM.getField(this.modelType, field), operator, value));
		
		return this;
	}
	
	public ModelFind<E> order(String field) throws InvalidModelField
	{
		return this.order(field, OrderDirection.ASC);
	}
	
	public ModelFind<E> order(String field, OrderDirection direction) throws InvalidModelField
	{
		
		this.orderBy = new QueryOrderBy(OrderDirection.ASC, new Field[]{ ORM.getDataField(this.modelType, field) });
		
		return this;
	}
	
	public ModelFind<E> order(String[] fieldNames) throws InvalidModelField
	{
		return order(fieldNames, OrderDirection.ASC);
	}
	
	public ModelFind<E> order(String[] fieldNames, OrderDirection direction) throws InvalidModelField
	{
		Field[] fields = new Field[fieldNames.length];
		
		int index = 0;
		for (String fieldName: fieldNames)
		{
			fields[index++] = ORM.getDataField(this.modelType, fieldName);
		}
		
		this.orderBy = new QueryOrderBy(OrderDirection.ASC, fields);
		
		return this;
	}
	
	public E execute()
	{
		try 
		{
			this.dbInterface.openDB();
			
			if (this.whereList.size() == 0)
			{
				try 
				{
					if (this.model.getId() > -1)
					{
						this.where("id", WhereOperator.EQUAL, this.model.getId()+"");
					}
					else if (this.model.getParentId() > -1)
					{
						this.where(this.model.getParentName(), WhereOperator.EQUAL, this.model.getParentId()+"");
					}
				} 
				catch (InvalidModelField e) 
				{
					e.printStackTrace();
				}
			}
			
			String table = ORM.tableName(this.modelType);
			String where = QueryWhere.whereString(this.whereList);
			String[] whereArgs = QueryWhere.whereArgs(this.whereList);
			String orderBy = QueryOrderBy.orderByString(this.orderBy);
			
			Log.d("orm", "Finding " + this.modelType.getSimpleName());
			if (where != null)
			{
				Log.d("orm", "Where: " + where);
				for (String arg: whereArgs)
				{
					Log.d("orm", arg);
				}
			}
			if (orderBy != null)
			{
				Log.d("orm", "Order By: " + orderBy);
			}
			
			Cursor cursor = this.dbInterface.getDB().query(table, null, where, whereArgs, null, null, orderBy);
			if (cursor.moveToFirst())
			{
				this.model.fromCursor(cursor);
				
				this.model.setUpdated(false);
				this.model.setLoaded(true);
				
				cursor.close();
				
				this.dbInterface.closeDB();
				
				return model;
			}
		
		} 
		catch (IllegalArgumentException e) 
		{
			e.printStackTrace();
		} 
		catch (IllegalAccessException e) 
		{
			e.printStackTrace();
		} 
		catch (NoSuchMethodException e) 
		{
			e.printStackTrace();
		} 
		catch (InstantiationException e) 
		{
			e.printStackTrace();
		} 
		catch (InvocationTargetException e) 
		{
			e.printStackTrace();
		} 
		catch (InvalidModelRelationship e) 
		{
			e.printStackTrace();
		}
		
		this.dbInterface.closeDB();
		
		return null;
	}
	
}
